#include "project_config.h"

#if (SYSLOG_ENABLE == 1)

#include "qelib.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"
#include "syslog.h"

QELOG_DOMAIN("syslog");

typedef struct
{
    char *name;
    void *user;
    syslog_output_handle handle;
    qe_list list;
} syslog_output;

static QueueHandle_t queue;
static EventGroupHandle_t init_done = QE_NULL;
static QE_LIST_INIT(outputs);

typedef struct
{
    char data[SYSLOG_MSG_MAX_SIZE];
    int size;
} syslog_msg;

static void log_to_queue(const char *message)
{
    BaseType_t woken = pdFALSE;
    syslog_msg msg;
    qe_memset(&msg, 0, sizeof(msg));
    qe_memcpy(msg.data, message, qe_strlen(message));
    msg.size = qe_strlen(message);

   if (qe_is_interrupt_context() != 0) {
       xQueueSendFromISR(queue, &msg, &woken);
       portYIELD_FROM_ISR(woken);
   } else {
       xQueueSend(queue, &msg, 0);
   }
}

static syslog_output *syslog_find_output(qe_const_str name)
{
    syslog_output *p;

    qe_list_foreach_entry(p, &outputs, list) {
        if (qe_strcmp(name, p->name) == 0) {
            return p;
        }
    }

    return QE_NULL;
}

static void syslog_do_output(char *data, int size)
{
    syslog_output *p;

    qe_list_foreach_entry(p, &outputs, list) {
        p->handle(data, size, p->user);
    }
}

static void syslog_task(void *args)
{
    syslog_msg msg;

    qelog_set_default_output(log_to_queue);

    xEventGroupSetBits(init_done, 0x01);
    
    qe_notice("syslog run");

    while (1) {

        if (xQueueReceive(queue, &msg, portMAX_DELAY) == pdPASS) {
            syslog_do_output(msg.data, msg.size);
        }
    }
}

static qe_init int syslog_init()
{
    init_done = xEventGroupCreate();
    queue = xQueueCreate(SYSLOG_QUEUE_NUM, sizeof(syslog_msg));
    xTaskCreate(syslog_task, "syslog", 0x400, QE_NULL, 2, QE_NULL);
    return 0;
}
QE_ENV_EXPORT(syslog_init);

void syslog_initdone_wait()
{
    EventBits_t bits;

    while (1) {
        if (init_done != QE_NULL) {
        	bits = xEventGroupWaitBits(init_done, 0xf, pdFALSE, pdFALSE, portMAX_DELAY);
            if (bits & 0x01) {
                break;
            }
        }
        qe_msleep(1000);
    }
}

void syslog_add_output_handle(qe_const_str name, syslog_output_handle handle, void *user)
{
    syslog_output *output;
    
    if (!name || !handle) {
        qe_error("invalid params");
        return;
    }
    
    output = syslog_find_output(name);
    if (output) {
        qe_warning("output %s exist", name);
        return;
    }

    output = qe_malloc(sizeof(syslog_output));
    if (!output) {
        qe_error("alloc mem for output failed");
        return;
    }

    output->name = qe_strdup(name);
    output->handle = handle;
    output->user = user;

    qe_list_append(&output->list, &outputs);
    qe_info("add output %s %p", name, handle);
}

void syslog_remove_output_handle(qe_const_str name)
{
    syslog_output *output = syslog_find_output(name);
    if (!output) {
        qe_error("output %s not found", name);
        return;
    }

    qe_info("remove output %s %p", name, output->handle);

    qe_list_remove(&output->list);
    qe_free(output->name);
    qe_free(output);
}
#endif
